package wortubung.lang.fr;

import java.util.HashMap;

import org.json.JSONArray;
import org.json.JSONObject;

import wortubung.core.LANG;
import wortubung.inflection.CONJUGATION;
import wortubung.inflection.ConjugationResult;
import wortubung.inflection.ConjugationSet;
import wortubung.inflection.DerivedVerbConjugation;
import wortubung.inflection.VerbConjugation;
import wortubung.util.ImmutableStringList;

import static wortubung.lang.ConjugatorBase.getRomanString;
import static wortubung.lang.fr.ConjugueurUtil.*;
import static wortubung.util.LatinScript.*;
import static wortubung.util.CommonUtils.*;
import static wortubung.util.JSONUtils.*;

/**
 * Le conjugueur par défaut utilise ConjugueurBase.participesIrreguliers et
 * ConjugueurBase.futurRacines a ces fins. Alors definis des valeurs la-bas
 * dans le constructeur.
 * 
 * Il semble que la plupart des verbes irréguliers suivent la conjugaison de RE,
 * donc l'implémentation par défaut fait juste cela.
 */
public class ConjugueurIrregulier implements VerbConjugation {

	public enum V {
		// régle de variation:
		// La 1ere partie est pour présent;
		// La 2e partie pour passe simple/subj. imparfait
		s_I, s_U,
		S_I, S_U, // S_I est le defaut
		X_I, X_U,
		x_U,      // pouvoir, vouloir
		E_I,      // assaillir, cueillir, offrir
		S_A,      // faire
		s_N,      // venir, tenir
		S_Iu;     // S_I mais participe passé est comme *_U.

		boolean presentER()    { return this == E_I; }
		boolean etreA()        { return this == S_A; }
		boolean etreN()        { return this == s_N; }
		boolean etreU()        { return name().endsWith("U"); }
		boolean etreX()        { char c = name().charAt(0); return c == 'X' || c == 'x'; }
		boolean ilsCommeNous() { return name().charAt(0) != 's' && !ilsCommeVOULOIR();  }
		boolean ilsCommeVOULOIR() { return name().charAt(0) == 'x';  } // et POUVOIR
		boolean ppU()          { return etreU() || etreN() || this == S_Iu; }
	};

	protected String verb;
	protected String meaning;
	protected boolean isSuffixOnly;
	protected Integer refIndex1;
	protected Integer refIndex2;

	protected V variation = V.S_I;
	protected String presentSingleRacine;
	protected String presentPlurielRacine;
	protected String passeRacine;
	protected String subjPreSing;
	protected String subjPrePlur;

	protected String participePresent;
	protected String participePasse;

	/**
	 * Usually this is set in ConjugueurBase.futurRacines.
	 * But for verbs with alternative conjugation, we have to set it for one.
	 */
	protected String futurRacine;

	/**
	 * For "faillir" only, as one of its alternative is a regular conjugation.
	 */
	protected boolean etreRegulier;

	protected HashMap<CONJUGATION,String[]> overrides;
	// "_" means skip override. String[0] means remove.

	protected VerbConjugation alternative;

	protected ConjugueurIrregulier() {
	}

	/**
	 * Le constructeur principal
	 * 
	 * @param participePresent ___ pour défaut
	 * @param participePasse   ___ pour défaut
	 * @param variation
	 * @param racines is { presentSingleRacine,
	 *                     presentPlurielRacine (et passeRacine = presentPlurielRacine + "__"),
	 *                     passeRacine,
	 *                     subjPreSing et subjPrePlur,
	 *                     subjPrePlur }
	 */
	protected ConjugueurIrregulier(String verb, Integer refIndex1, Integer refIndex2, String participePresent, String participePasse, V variation, String[] racines) {
		this.verb = verb;
		this.refIndex1 = refIndex1;
		this.refIndex2 = refIndex2;

		this.variation = variation;
		presentSingleRacine  = chopRight(verb, verb.endsWith("oir") ? 3 : 2);
		presentPlurielRacine = chopRight(verb, 2);
		passeRacine = presentPlurielRacine + "__";
		if (racines != null) {
			if (racines[0] != null)
        		presentSingleRacine  = racines[0];
			if (racines.length > 1 && racines[1] != null) {
        		presentPlurielRacine = racines[1];
        		passeRacine = presentPlurielRacine + "__";
			}
			if (racines.length > 2 && racines[2] != null)
        		passeRacine = racines[2];
			if (racines.length > 3) {
				subjPreSing = subjPrePlur = racines[3];
				if (racines.length > 4 && racines[4] != null)
					subjPrePlur = racines[4];
			}
		}

		if (___.equals(participePresent)) {
			participePresent = phonetique(presentPlurielRacine, "ant", true);
		}
		if (___.equals(participePasse)) {
			participePasse = phonetique(chopRight(passeRacine, 2), variation.ppU() ? 'u' : 'i', true);
			if (participePasse.endsWith("û"))
				participePasse += '|' + chopRight(participePasse, 1) + 'e';
		}
		this.participePresent = participePresent;
		this.participePasse = participePasse;
	}

	public void setMeaning(String meaning) { this.meaning = meaning; }
	public void setAlternative(VerbConjugation alternative) { this.alternative = alternative; }

	public VerbConjugation conjugate(String prefix, String verbeSuffix) {
		if (etreRegulier) // only "faillir"
			return Conjugueur.defaultConjugator.conjugate(null, verbeSuffix);
		return conjugate_(prefix, verbeSuffix);
	}

	private VerbConjugation conjugate_(String prefix, String verbeSuffix) {
		ConjugationResult ret = new ConjugationResult(verbeSuffix);
		ConjugationSet cs = new ConjugationSet(ret, participle(verbeSuffix, true), participle(verbeSuffix, false));
		CONJUGATION[] conjs = CONJUGATION.forLanguage(LANG.fr);
		for (CONJUGATION type : conjs) {
			String[] conj = conjuguer(verbeSuffix, type, true);
			if (conj != null)
				cs.add(type, conj);
		}
		ret.addConjugationSet(cs);
		return (prefix == null) ? ret : new DerivedVerbConjugation(prefix, ret);
	}

	protected String participle(String verbeSuffix, boolean isPresent) {
		return isPresent ? participePresent : participePasse;
	}

	public String[] conjuguerPresent(String verbe) {
		if (variation.presentER())
			return conjuguerER(verbe, CONJUGATION.PRESENT, true);
    	return conjuguerPresentSX(aRacines(presentSingleRacine, presentPlurielRacine, variation.ilsCommeNous(), variation.ilsCommeVOULOIR()), variation.etreX());
	}

	public String[] conjuguer(String verb, CONJUGATION type, boolean fairePhonetique) {
		switch(type) {
		case PRESENT:
			String[] ret = conjuguerPresent(verb);
			if (verb.endsWith("aître"))
				ret[2] = chopRight(ret[2], 2) + "ît";
			return ret;
		case SUBJ_PRESENT:
			if (variation.etreA()) // seulement 'faire'
				return conjuguerA_(passeRacine, type);
			if (subjPreSing != null)
				return phonetique(aRacines(subjPreSing, subjPrePlur, false, false), SUBJ_PRE_ER_INDICES, true);
		case IMPERFECT:
			return conjuguerER(presentPlurielRacine + "__", type, true);
		case SIMPLE_PAST:
		case SUBJ_IMPERFECT:
			if (variation.etreU())
				return conjuguerU_(passeRacine, type);
			if (variation.etreN()) // seulement 'venir' et 'tenir'
				return conjuguerIN(passeRacine, type);
			return conjuguerIR(passeRacine, type);
		case FUTURE:
		case CONDITIONAL:
			if (futurRacine != null)
				return appendToRoot(futurRacine, (type == CONJUGATION.FUTURE) ? FUTUR_INDICES : IMPARFAIT_ER_INDICES);
			return apposerIndiceFuturConditionnel(verb, fairePhonetique, type == CONJUGATION.FUTURE);
		case IMPERATIVE:
			return imperatif(verb, fairePhonetique);
		default:
			return null;
		}
	}

	protected String[] imperatif(String verbeSuffix, boolean fairePhonetique) {
		String[] resulte = conjuguer(verbeSuffix, CONJUGATION.PRESENT, fairePhonetique);
		imperatifDePresent(resulte);
		return resulte;
	}

	public static ImmutableStringList aRacines(String racineSing, String racinePlur, boolean ilsCommeNous, boolean ilsCommeVOULOIR) {
		String racineIls;
		if (ilsCommeVOULOIR)
			racineIls = racineSing + last(racinePlur, racinePlur.length()-racineSing.length());
		else
			racineIls = ilsCommeNous ? racinePlur : racineSing;
		return isl(racineSing, racineSing, racineSing, racinePlur, racinePlur, racineIls);
	}

	@Override public String getVerb() { return verb; }
	@Override public boolean isSuffixOnly() { return isSuffixOnly; }
	@Override public boolean isRootSuffixOnly() { return false; }
	@Override public String getPrefix() { return null; }
	@Override public String getDerivedFrom() { return null; }
	@Override public boolean hasAlternative() { return alternative != null; }
	@Override public Integer getRefIndex1() { return refIndex1; }
	@Override public Integer getRefIndex2() { return refIndex2; }
	@Override public String getMeaning() { return meaning; }
	@Override public String[] getUsage() { return null; }

	@Override public ConjugationResult getConjugationResult(LANG langForLatinizing) {
		ConjugationResult ret = conjugate(null, verb).getConjugationResult(langForLatinizing);
		ret.setRefIndex1(getRefIndex1());
		ret.setRefIndex2(getRefIndex2());

		ret.getConjugationResult(langForLatinizing).getConjugations().get(0).override(overrides);

		if (alternative != null) {
			ConjugationResult alt = alternative.getConjugationResult(langForLatinizing);
			ret.getConjugations().addAll(alt.getConjugations());
		}
		return ret;
	}

	// JSON data

	public static final String VERBE               = "VERBE";
	public static final String SUFFIX              = "SUFFIX";
	public static final String REF_INDICES         = "REF_INDICES";
	public static final String VARIATION           = "VARIATION";
	public static final String PARTICIPE_PRESENT   = "PARTICIPE_PRESENT";
	public static final String PARTICIPE_PASSE     = "PARTICIPE_PASSE";
	public static final String PRESENT_RACINE      = "PRESENT_RACINE";
	public static final String PASSE_RACINE        = "PASSE_RACINE";
	public static final String FUTUR_RACINE        = "FUTUR_RACINE";
	public static final String SUBJ_PRESENT_RACINE = "SUBJ_PRESENT_RACINE";
	public static final String OVERRIDE            = "OVERRIDE";
	public static final String ALTERNATIF          = "ALTERNATIF";
	public static final String ETRE_REGULIER       = "ETRE_REGULIER"; // only for "faillir", its alternative.

	public static ConjugueurIrregulier getRuleFromJSON(JSONObject json) {
		return ruleFromJSON(json);
	}

	private static ConjugueurIrregulier ruleFromJSON(JSONObject json) {
		String verb;
		boolean isSuffixOnly = false;
		Integer[] refIndices;
		V variation;
		String participePresent;
		String participePasse;
		String passeRacine;
		String presentSingleRacine = null;
		String presentPlurielRacine = null;
		String subjPreSing = null;
		String subjPrePlur = null;

		// read from json
		verb = getRomanString(json, VERBE, null);
		if (verb == null) {
			verb = getRomanString(json, SUFFIX, null);
			isSuffixOnly = true;
		}
		refIndices = getIntArray(json, REF_INDICES);
		variation = V.valueOf(getString(json, VARIATION, "S_I"));
		participePresent = getRomanString(json, PARTICIPE_PRESENT, ___);
		participePasse   = getRomanString(json, PARTICIPE_PASSE, ___);
		passeRacine      = getRomanString(json, PASSE_RACINE, null);
		if (passeRacine != null)
			passeRacine += "__";

		Object o;
		if (json.has(PRESENT_RACINE)) {
			o = json.opt(PRESENT_RACINE);
			if (o instanceof String) {
				presentSingleRacine = presentPlurielRacine = enAccent((String)o);
			} else if (o instanceof JSONArray) {
				presentSingleRacine  = enAccent(((JSONArray)o).getString(0));
				presentPlurielRacine = enAccent(((JSONArray)o).getString(1));
			}
		}
		if (json.has(SUBJ_PRESENT_RACINE)) {
			o = json.get(SUBJ_PRESENT_RACINE);
			if (o instanceof String) {
				subjPreSing = subjPrePlur = enAccent((String)o);
			} else if (o instanceof JSONArray) {
				subjPreSing = enAccent(((JSONArray)o).getString(0));
				subjPrePlur = enAccent(((JSONArray)o).getString(1));
			}
		}

		// set default values
		if (presentSingleRacine == null)
			presentSingleRacine = chopRight(verb, verb.endsWith("oir") ? 3 : 2);
		if (presentPlurielRacine == null)
			presentPlurielRacine = chopRight(verb, 2);
		if (passeRacine == null)
			passeRacine = presentPlurielRacine + "__";

		if (___.equals(participePresent))
			participePresent = phonetique(presentPlurielRacine, "ant", true);
		else if (participePresent != null && isEmpty(participePresent))
			participePresent = null;

		if (___.equals(participePasse)) {
			participePasse = phonetique(chopRight(passeRacine, 2), variation.ppU() ? 'u' : 'i', true);
			if (participePasse.endsWith("û"))
				participePasse += '|' + chopRight(participePasse, 1) + 'e';
		}

		ConjugueurIrregulier ci = new ConjugueurIrregulier();
		ci.verb = verb;
		ci.isSuffixOnly = isSuffixOnly;
		ci.refIndex1 = refIndices[0];
		ci.refIndex2 = refIndices[1];
		ci.variation = variation;
		ci.presentSingleRacine = presentSingleRacine;
		ci.presentPlurielRacine = presentPlurielRacine;
		ci.passeRacine = passeRacine;
		ci.subjPreSing = subjPreSing;
		ci.subjPrePlur = subjPrePlur;
		ci.participePresent = participePresent;
		ci.participePasse = participePasse;

		// read override
		if (json.has(OVERRIDE)) {
			ci.overrides = new HashMap<CONJUGATION,String[]>();
			JSONObject jsonOver = json.getJSONObject(OVERRIDE);
			for (Object key : jsonOver.keySet()) {
				String[] sa = getStringArray(jsonOver, key.toString());
				enAccent(sa);
				ci.overrides.put(CONJUGATION.valueOf(key.toString()), sa);
			}
		}

		// for alternatif's
		if (json.has(FUTUR_RACINE))
			ci.futurRacine = enAccent(json.getString(FUTUR_RACINE));
		if (json.has(ETRE_REGULIER))
			ci.etreRegulier = json.getBoolean(ETRE_REGULIER);

		// read alternatif
		if (json.has(ALTERNATIF))
			ci.setAlternative(ruleFromJSON(json.getJSONObject(ALTERNATIF)));

		return ci;
	}
}
